﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;


using RayDen.Library.Core;

namespace RayDen.Library.Data.Scene.SCFF
{
    public class SceneConfiguration
    {
        private const string _mattematerialName = "mattematerial";
        private const string _specularmaterialName = "specularmaterial";
        private const string _metalmaterialName = "metalmaterial";
        private const string _glassmaterialName = "glassmaterial";

        protected static char[] omitchars = new[] { ' ', '\r', '\n', '\t', '/' };

        private Dictionary<string, Dictionary<string, Dictionary<string, object>>> data;
        private static string _alloymaterialName = "alloymaterial";


        public object this[string @namespace, string type, string name]
        {
            get
            {
                try
                {
                    return data[@namespace.ToLowerInvariant()][type.ToLowerInvariant()][name.ToLowerInvariant()];
                }
                catch (IndexOutOfRangeException)
                {
                    return null;
                }
            }

            set
            {
                try
                {
                    if (!data[@namespace.ToLowerInvariant()][type.ToLowerInvariant()].ContainsKey(name.ToLowerInvariant()))
                    {
                        data[@namespace.ToLowerInvariant()][type.ToLowerInvariant()][name.ToLowerInvariant()] = value;
                    }
                    else
                    {
                        data[@namespace.ToLowerInvariant()][type.ToLowerInvariant()][name.ToLowerInvariant()] = value;
                    }
                }
                catch (IndexOutOfRangeException ex)
                {
                    Console.WriteLine(ex.Message);
                    return;
                }
            }
        }


        #region EntityCollections

        public IEnumerable<ConfigEntity> Lights
        {
            get
            {
                return PointLights.Union(AreaLights.Cast<ConfigEntity>()).Union(InfiniteLights);
            }
        }

        public IEnumerable<ConfigEntity> Materials
        {
            get
            {
                return this.MatteMaterials.Cast<ConfigEntity>()
                           .Union(this.MetalMaterials)
                           .Union(this.MirrorMaterials)
                           .Union(this.AlloyMaterials)
                           .Union(this.GlassMaterials)
                    //.Union((EntityCollection) data["scene"]["material"]["archglassmaterial"])
                    //.Union((EntityCollection) data["scene"]["material"]["distribution"])
                           ;
            }
        }

        public EntityCollection PinholeCameras
        {
            get { return (EntityCollection)data["scene"]["camera"]["pinhole"]; }
        }

        public EntityCollection FiniteAppertureCameras
        {
            get { return (EntityCollection)data["scene"]["camera"]["finiteapperture"]; }
        }

        public EntityCollection PointLights
        {
            get { return (EntityCollection)data["scene"]["light"]["point"]; }
        }

        public EntityCollection AreaLights
        {
            get { return (EntityCollection)data["scene"]["light"]["area"]; }
        }

        public EntityCollection InfiniteLights
        {
            get { return (EntityCollection)data["scene"]["light"]["infinite"]; }
        }

        public EntityCollection LightsProfiles
        {
            get { return (EntityCollection)data["scene"]["light"]["profiles"]; }
        }

        public EntityCollection MatteMaterials
        {
            get { return (EntityCollection)data["scene"]["material"][_mattematerialName]; }
        }

        public EntityCollection MirrorMaterials
        {
            get { return (EntityCollection)data["scene"]["material"][_specularmaterialName]; }
        }

        public EntityCollection AlloyMaterials
        {
            get { return (EntityCollection)data["scene"]["material"][_alloymaterialName]; }
        }

        public EntityCollection MetalMaterials
        {
            get { return (EntityCollection)data["scene"]["material"][_metalmaterialName]; }
        }

        public EntityCollection GlassMaterials
        {
            get { return (EntityCollection)data["scene"]["material"][_glassmaterialName]; }
        }

        #endregion

        public object ReadFrame(string name)
        {
            if (!this.data["frame"]["global"].ContainsKey(name))
            {
                return null;
            }

            return this["frame", "global", name];
        }

        public void SetFrame(string name, object value)
        {
            this["frame", "global", name] = value;
        }

        public void SetMesh(string name, object value)
        {
            this["scene", "mesh", name] = value;
        }

        public object ReadScene(string type, string name)
        {
            return this["scene", type, name];
        }


        public SceneConfiguration()
        {
            CreateSceneStorage(out data);
        }

        public void Load(string filename)
        {
            var reader = new StreamReader(filename);

            while (!reader.EndOfStream)
            {
                string line = reader.ReadLine();

                if (string.IsNullOrEmpty(line))
                    continue;

                string[] tokens = line.Split(omitchars, StringSplitOptions.RemoveEmptyEntries);

                if (tokens.Length == 0 || tokens[0].StartsWith("#"))
                    continue;

                ParseLine(line, tokens);
            }
        }


        public void Save(string fileName)
        {
            using (var stream = new FileStream(fileName, FileMode.Create, FileAccess.Write))
            using (TextWriter writer = new StreamWriter(stream))
            {
                writer.WriteLine("# Ray Engine Scene Configuration file");
                writer.WriteLine();
                string prefix = "frame.";

                foreach (var frameGlobals in data["frame"]["global"])
                {
                    writer.WriteLine("{0} {1}", prefix + frameGlobals.Key, frameGlobals.Value);
                }

                prefix = "scene.mesh.";
                foreach (var frameGlobals in data["scene"]["mesh"])
                {
                    writer.WriteLine("{0} {1}", prefix + frameGlobals.Key, frameGlobals.Value);
                }

                prefix = "scene.camera.pinhole.";
                foreach (var frameGlobals in PinholeCameras)
                {
                    prefix += frameGlobals.Key;
                    foreach (var item in frameGlobals.Value)
                    {
                        writer.WriteLine("{0}.{1} {2}", prefix, item.Key, item.Value);
                    }
                }

                prefix = "scene.camera.finiteapperture.";
                foreach (var frameGlobals in FiniteAppertureCameras)
                {
                    writer.WriteLine("{0} {1}", prefix + frameGlobals.Key, frameGlobals.Value);
                }

                prefix = "scene.light.";
                foreach (var frameGlobals in Lights)
                {
                    foreach (var param in frameGlobals.GetParams())
                    {
                        var value = param.Value.ToString().Contains(" ") ? "\"" + param.Value + "\"" : param.Value.ToString();
                        writer.WriteLine("{0}{1}.{2}.{3} {4}", prefix, frameGlobals.Type, frameGlobals.Name, param.Key, value);
                    }
                }

                prefix = "scene.material.";
                foreach (var frameGlobals in Materials)
                {
                    foreach (var param in frameGlobals.GetParams())
                    {
                        writer.WriteLine("{0}{1}{2}{3} {4}", prefix, frameGlobals.Type, frameGlobals.Name, param.Key, param.Value);
                    }
                }

            }
        }


        public void Print()
        {
            foreach (var global in data)
            {
                PrintDictionary(global.Key, global.Value);
            }
        }

        private void ParseLine(string line, string[] tokens)
        {
            try
            {
                var path = tokens[0].Split('.');
                if (path[0] == "frame")
                {
                    this["frame", "global", path[1]] = tokens[1];
                } else 

                if (path[0] == "scene")
                {
                    if (path.Length > 3)
                    {
                        var entity = (this["scene", path[1], path[2]] as EntityCollection);
                        if (entity != null)
                        {
                            entity[path.Aggregate(4, ".")] = tokens.Aggregate(2, ",");
                        }
                    }
                    else if (path.Length == 3)
                    {
                        this["scene", path[1], path[2]] = tokens[1];
                    }
                    else
                    {
                        this["scene", "global", path[1]] = tokens[1];
                    }
                }

                else
                {
                    Console.WriteLine(path[0]);
                }
            }
            catch (Exception ex)
            {

                Console.WriteLine("Error parsing scene configuration : ");
                Console.WriteLine("Error line : " + line);
                Console.WriteLine(ex.Message + ex.StackTrace);

            }
        }

        private static void CreateSceneStorage(out Dictionary<string, Dictionary<string, Dictionary<string, object>>> data)
        {
            data =
                new Dictionary<string, Dictionary<string, Dictionary<string, object>>>(StringComparer.InvariantCultureIgnoreCase)
                    {
                        {
                            "frame",
                            new Dictionary<string, Dictionary<string, object>>(StringComparer.InvariantCultureIgnoreCase)
                                {
                                    {
                                        "global", new Dictionary<string, object>(StringComparer.InvariantCultureIgnoreCase)
                                            {
                                                {"width", 1280},
                                                {"height", 720},
                                            }
                                    }
                                }
                        },
                        {
                            "scene",
                            new Dictionary<string, Dictionary<string, object>>(StringComparer.InvariantCultureIgnoreCase)
                                {
                                    {
                                        "global", new Dictionary<string, object>(StringComparer.InvariantCultureIgnoreCase)
                                    },
                                    {"mesh", new Dictionary<string, object>(StringComparer.InvariantCultureIgnoreCase)},
                                    {
                                        "camera",
                                        new Dictionary<string, object>(StringComparer.InvariantCultureIgnoreCase)
                                            {
                                                {
                                                    "pinhole",new EntityCollection("pinhole")
                                                },
                                                {
                                                    "finiteapperture",new EntityCollection("fa")
                                                }
                                            }
                                    },
                                    {
                                        "light",
                                        new Dictionary<string, object>(StringComparer.InvariantCultureIgnoreCase)
                                            {
                                                {
                                                    "point", new EntityCollection("point")
                                                },
                                                {
                                                    "area", new EntityCollection("area")
                                                },
                                                {
                                                    "infinite",new EntityCollection("infinite")
                                                },
                                                {
                                                    "spheric",new EntityCollection("spheric")
                                                },
                                                { 
                                                    "profiles", new EntityCollection("profile")
                                                }
                                            }
                                    },
                                    {
                                        "material",
                                        new Dictionary<string, object>(StringComparer.InvariantCultureIgnoreCase)
                                            {
                                                {
                                                    _alloymaterialName,new EntityCollection("alloy")
                                                },
                                                {
                                                    _mattematerialName,
                                                    new EntityCollection("matte")
                                                },
                                                {
                                                    _metalmaterialName,
                                                    new EntityCollection("metal")
                                                },
                                                {
                                                    _specularmaterialName,
                                                    new EntityCollection("specular")
                                                },
                                                {
                                                    _glassmaterialName,
                                                    new EntityCollection("glass")
                                                },
                                                {
                                                    "distributionmaterial",
                                                    new EntityCollection("distr")
                                                },
                                                {
                                                    "measuredmaterial",
                                                    new EntityCollection("measured")
                                                },
                                                {
                                                    "orennayarmaterial",
                                                    new EntityCollection("on")
                                                },
                                                {
                                                    "aphongmaterial",
                                                    new EntityCollection("aphong")
                                                },
                                                {
                                                    "mattemirrormaterial",
                                                    new EntityCollection("mattemirror")
                                                },
                                            }
                                    },
                                }
                        }
                    };
        }

        private void PrintDictionary<T>(string head, IDictionary<string, T> dict)
            where T : IDictionary
        {
            Console.WriteLine(head);
            if (dict is Dictionary<string, object>)
            {
                var dic = dict as Dictionary<string, object>;
                foreach (var item in dic)
                {
                    Console.WriteLine("{0}-{1}-{2}", item.Key, item.Value.GetType().Name, item.Value);
                }
            }
            else if (dict is Dictionary<string, Dictionary<string, object>>)
            {
                var dic = dict as Dictionary<string, Dictionary<string, object>>;
                Console.WriteLine(head + dic);
                foreach (var dictItem in dic)
                {

                }
            }
        }

        private static void PrintDictionary(string head, Dictionary<string, object> dic)
        {
            Console.WriteLine(head);
            foreach (var item in dic)
            {
                Console.WriteLine("{0}-{1}-{2}", item.Key, item.Value.GetType().Name, item.Value);
            }
        }



    }
}
